package com.signway.ffmpeg;

import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;

import androidx.annotation.NonNull;

import java.io.IOException;
import java.nio.ByteBuffer;

public class VideoDecoder {
    private static final String TAG = VideoDecoder.class.getName();

    public static final String VIDEO = "video/";
    public static final String AUDIO = "audio/";

    private FFmpeg mFFmpeg = new FFmpeg();
    private String mMediaFile = null;
    private MediaCodec mMediaCodec = null;
    private MediaExtractor mMediaExtractor = null;
    private MediaFormat mMediaFormat = null;
    private Handler mVideoDecoderHandler = null;
    private HandlerThread mVideoDecoderHandlerThread = new HandlerThread("VideoDecoder");

    private MediaCodec.Callback mCallback = new MediaCodec.Callback() {

        @Override
        public void onInputBufferAvailable(@NonNull MediaCodec mediaCodec, int id) {
            ByteBuffer inputBuffer = mediaCodec.getInputBuffer(id);
            if (mMediaExtractor.getSampleFlags() >= 0) {
                int sampleSize = mMediaExtractor.readSampleData(inputBuffer, 0);
                long sampleTimeUs = mMediaExtractor.getSampleTime();
                mediaCodec.queueInputBuffer(id, 0, sampleSize, sampleTimeUs, 0);
                mMediaExtractor.advance();
                Log.d(TAG, "onInputBufferAvailable: " + sampleTimeUs);
            } else {
                mediaCodec.queueInputBuffer(id, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                Log.d(TAG, "onInputBufferAvailable: END");
            }
        }

        @Override
        public void onOutputBufferAvailable(@NonNull MediaCodec mediaCodec, int id, @NonNull MediaCodec.BufferInfo bufferInfo) {
            ByteBuffer outputBuffer = mMediaCodec.getOutputBuffer(id);
            if (outputBuffer != null && bufferInfo.size > 0) {
                mFFmpeg.onOutputBufferAvailable(outputBuffer, bufferInfo);
            }
            Log.d(TAG, "onOutputBufferAvailable: pts " + bufferInfo.presentationTimeUs + ", flag " + bufferInfo.flags + ", size " + bufferInfo.size);
            mMediaCodec.releaseOutputBuffer(id, false);
        }

        @Override
        public void onError(@NonNull MediaCodec mediaCodec, @NonNull MediaCodec.CodecException e) {
            Log.d(TAG, "onError: ");
        }

        @Override
        public void onOutputFormatChanged(@NonNull MediaCodec mediaCodec, @NonNull MediaFormat mediaFormat) {
            Log.d(TAG, "onOutputFormatChanged: ");
        }
    };

    public VideoDecoder(String mediaFile) throws IOException {
        mMediaFile = mediaFile;
        mMediaExtractor = new MediaExtractor();
        mMediaExtractor.setDataSource(mMediaFile);
        Log.d(TAG, "VideoDecoder: setDataSource " + mMediaFile);
        for (int i = 0; i < mMediaExtractor.getTrackCount(); i++) {
            mMediaFormat = mMediaExtractor.getTrackFormat(i);
            String mime = mMediaFormat.getString(MediaFormat.KEY_MIME);
            if (mime.startsWith(VIDEO)) {
                mMediaExtractor.selectTrack(i);
                mMediaCodec = MediaCodec.createDecoderByType(mime);
                break;
            }
        }

        mVideoDecoderHandlerThread.start();
        mVideoDecoderHandler = new Handler(mVideoDecoderHandlerThread.getLooper());
    }

    public void startDecoder() {
        if (mMediaCodec != null) {
            mMediaCodec.setCallback(mCallback, mVideoDecoderHandler);
            mMediaCodec.configure(mMediaFormat, null, null, 0 /* Decoder */);
            mMediaCodec.start();

            mFFmpeg.createEncoder(mMediaFormat);
        } else {
            throw new IllegalArgumentException("startDecoder failed, please check the MediaCodec is init correct");
        }
    }

    public void stopDecoder() {
        if (mMediaCodec != null) {
            mMediaCodec.stop();
        } else {
            throw new IllegalArgumentException("startDecoder failed, please check the MediaCodec is init correct");
        }
    }

    public void releaseDecoder() {
        if (mMediaCodec != null) {
            mMediaCodec.release();
            mMediaCodec = null;
        } else {
            throw new IllegalArgumentException("startDecoder failed, please check the MediaCodec is init correct");
        }
    }
}
